package coin

import (
	"global/core"
	"github.com/parnurzeal/gorequest"
	"time"
	"global/constants"
	"global/libs/structs"
	"encoding/json"
	"github.com/ngaut/log"
	"strings"
	"strconv"
	"os"
	"global/model"
	"global/util"
	"math"
	"srv/cmc/cap"
)

type CoinMetaData struct {
	Ctx 		*core.Context
	Client 		*gorequest.SuperAgent
	Key 		string
	IntervalC 	*time.Ticker
}

func NewCoinMetaData() cap.InterfaceCmc {
	return &CoinMetaData{
		IntervalC: 		time.NewTicker(1 * time.Hour),
	}
}

func (e *CoinMetaData) Init(ctx *core.Context) cap.InterfaceCmc {
	e.Ctx = ctx
	e.Client = gorequest.New()
	e.Key = e.Ctx.AccountConfig()
	return e
}

func (e *CoinMetaData) Run() error {
	e.handler()
	return nil
}

func (e *CoinMetaData) handler()  {
	coinMaps, err := e.getCmcCoinIdMap()
	if err != nil {
		util.ErrorAndExit("Error: %s", err.Error())
	}

	coinIds := make([]string, 0)
	for _, coin := range coinMaps {
		coinIds = append(coinIds, strconv.Itoa(coin.Id))
	}

	// 每组获取100个
	coins := make([][]string, 0)
	groupItems := math.Ceil(float64(len(coinIds)) / 100)
	for i := 0; i < int(groupItems); i++ {
		start := i * 100
		end := start + 100
		if end >= len(coinIds) {
			end = len(coinIds)
		}
		tmp := coinIds[start:end]
		coins = append(coins, tmp)
	}

	for _, v := range coins {
		if err := e.doGetCoinInfo(v); err != nil {
			log.Errorf("Error: %s", err)
		}
	}

	os.Exit(1)
}

func (e *CoinMetaData) doGetCoinInfo(coinIds []string) error {
	coinInfos, err := e.getCmcCoinMetaData(coinIds)
	if err != nil {
		// util.ErrorAndExit("Error: %s", err.Error())
		return err
	}


	var datas []*model.Coin
	for _, item := range coinInfos {
		var webSite, twitter, explorer, chat, announcement, sourceCode, reddit, messageBoard string
		if len(item.Urls.Website) > 0 {
			webSite = item.Urls.Website[0]
		}

		if len(item.Urls.Twitter) > 0 {
			twitter = item.Urls.Twitter[0]
		}

		if len(item.Urls.Explorer) > 0 {
			explorer = item.Urls.Explorer[0]
		}

		if len(item.Urls.Chat) > 0 {
			chat = item.Urls.Chat[0]
		}

		if len(item.Urls.Announcement) > 0 {
			announcement = item.Urls.Announcement[0]
		}

		if len(item.Urls.SourceCode) > 0 {
			sourceCode = item.Urls.SourceCode[0]
		}

		if len(item.Urls.MessageBoard) > 0 {
			messageBoard = item.Urls.MessageBoard[0]
		}

		if len(item.Urls.Reddit) > 0 {
			reddit = item.Urls.Reddit[0]
		}

		logoFile, err := util.DownloadImg(item.Logo, strings.ToLower(item.Symbol), "coins")
		if err != nil {
			log.Errorf("Error: %s", err)
			logoFile = ""
		}

		datas = append(datas, &model.Coin{
			Mid: 			item.Id,
			NameCn: 		"",
			NameEn: 		item.Name,
			Symbol: 		item.Symbol,
			Slug: 			item.Slug,
			Logo: 			logoFile,
			Description: 	"",
			Category: 		item.Category,
			Tags: 			strings.Join(item.Tags, ","),
			Country: 		"",
			DateAdded: 		"",
			MaxSupply: 		"0",
			CirculatingSupply: 	"0",
			Website: 		webSite,
			Explorer: 		explorer,
			SourceCode: 	sourceCode,
			MessageBoard: 	messageBoard,
			Chat: 			chat,
			Announcement: 	announcement,
			Reddit: 		reddit,
			Twitter: 		twitter,
			WhitePaper: 	"",
			Sort: 			0,
			CreateTime: 	time.Now(),
			UpdateTime: 	time.Now(),
		})
	}

	success, err := model.CoinModel.InsertMulti(datas)
	if err != nil {
		// util.ErrorAndExit("Error: save coin market failed, msg: %s", err.Error())
		return err
	}

	log.Infof("get coin metadata complete, status: %v, len: %d", success, len(coinIds))
	return nil
}

// 获取交易所元数据
func (e *CoinMetaData) getCmcCoinMetaData(ids []string) (map[string]structs.CmcCoinMetaData, error) {
	_, body, errs := e.Client.
		Get(core.GetCmcUrl(e.Ctx.IsDebug(), constants.CmcCoinInfoUrl)).
		Set("X-CMC_PRO_API_KEY", e.Key).
		Query(map[string]interface{}{
			"id": strings.Join(ids, ","),
		}).
		End()

	if len(errs) != 0 {
		return nil, errs[0]
	}

	type bodyData struct {
		Status 			structs.CmcStatus					`json:"status"`
		Data 			map[string]structs.CmcCoinMetaData 	`json:"data"`
	}

	var parsedData bodyData
	err := json.Unmarshal([]byte(body), &parsedData)
	if err != nil {
		return nil, err
	}

	return parsedData.Data, nil
}

func (e *CoinMetaData) getCmcCoinIdMap() ([]structs.CmcCoinIdMap, error) {
	_, body, errs := e.Client.
		Get(core.GetCmcUrl(e.Ctx.IsDebug(), constants.CmcCoinMapUrl)).
		Set("X-CMC_PRO_API_KEY", e.Key).
		Query(map[string]interface{} {
			"limit": 5000,
		}).
		End()

	if len(errs) != 0 {
		return nil, errs[0]
	}

	type bodyData struct {
		Status 		structs.CmcStatus			`json:"status"`
		Data 		[]structs.CmcCoinIdMap 		`json:"data"`
	}

	var parsedBody bodyData
	err := json.Unmarshal([]byte(body), &parsedBody)
	if err != nil {
		return nil, err
	}

	return parsedBody.Data, nil
}